//: [Previous](@previous)
//: ## 字面量(Literal)
import Foundation
/*
 Swift自带的绝大部分类型, 都支持直接通过字面量进行初始化
 Bool/Int/Float/Double/String/Array/Dictionary/Set/Optional/等等..
 这些自带类型之所以能通过字面量初始化,是因为他们遵守了对应的协议
 比如:Bool: ExpressibleByBooleanLiteral
 var age = 10
 var isRed = false
 var name = "Jack"
 上面代码中的10,false,"Jack"就是字面量
 //需求举例: 对于一个Int类型赋值,赋值ture就是1,赋值false就是0,并且在赋值Bool值期间完成初始化
 extension Int: ExpressibleByBooleanLiteral {
     public init(booleanLiteral value: Bool) {
         self = value ? 1 : 0
         //true 返回1, false 返回0
     }
 }
 var num: Int = true
 print(num) //1
 */
//:## 模式
/*
 模式是用于匹配的规则, 比如Switch的case, 捕捉错误的catch, if/guard/while/for语句的条件等
 有:
 1, 通配符模式(Wildcard Pattern )
   1._ :匹配任何值
   2._? :匹配非nil值
 
 enum Life {
     case human(name: String, age: Int?)
     case animal(name: String, age: Int?)
 }
 func check(_ life: Life) {
     switch life {
     case .human(let name, _)://_ :代表一切,包括空值
         print("human", name)
     case .animal(let name, _?)://_? :代表非空,不包括空值
         print("animal", name)
     default:
         print("animal nil")
     }
 }
 
 2, 标识符模式(Identifier Pattern )
  给对应的变量/常量名赋值
  var age = 10
  let name = "jack"
 
 3, 值绑定模式(Value-Binding Pattern )
 let point = (3, 2)
 switch point {
 case let (x, y):
     print(x, y)
 }
 
 4, 元组模式(Tuple Pattern )
  就是利用元祖去赋值和判断进行一下方便的操作
 let points = [(0, 0), (1, 0), (2, 0)]
 for (x, _) in points {//利用元祖模式,把x取到了值,_是通配符代表任何值都无所谓
     print(x)
 }//0,1,2

 let name: String? = "Jack"
 let age = 18
 let info: Any = [1, 2]
 switch (name, age, info) {
 case (_?, _, _ as String):
     print("case")
 default:
     print("default")
 }//default

 var scores = ["Jack" : 98, "Rose" : 100, "Town": 99]
 //遍历字典的时候,用元祖模式,会自动左边key,右边value
 for (name, scores) in scores {
     print(name, scores)
 }
 
 5, 枚举Case模式(Enumeration Case Pattern )
 ⚠️只有有case的,就要马上想到Switch语法,带一个case的意思
 let age = 2
 //原来的写法
 if age >=0 && age <= 9 {
     print("[0, 9]")
 }
 //枚举case模式
 if case 0...9 = age {// 意思就是 拿age和case(case 0...9)比较, 看满足条件不. 这里的=等号其实是是否匹配的意思
     print("[0, 9]")
 }
 //if case这种写法 其实就是相当于只有一个case的Switch语句
 switch age {
 case 0...9:
     print("[0, 9]")
 default:
     break
 }

 //guard
 guard case 0...9 = age else { return }
 print("[0, 9]")

 let ages: [Int?] = [1, 2, 3, nil, 5]
 for case nil in ages {//在ages数组里面遍历元素, 符合 case nil(其实就是满足nil的意思),就进入大括号
     print("有nil值")
     break
 }
 
 let points = [(1,0), (2,1), (3,0)]
 for case let(x, 0) in points {
     print(x)
 }//1 3
 //👆🏻假如括号里面是是有带具体值的(比如0),就必须带case. 假如没有具体值的可以不用带.👇🏻这写没有具体数值的,就不需要用case
 for (x,y) in points {
     print((x, y))
 }//(1, 0) (2, 1) (3, 0)
 for (x,_) in points {
     print(x)
 }//1 2 3

 
 6, 可选模式(Optional Pattern )
 1.正常的可选项
 var age: Int? = 10

 2.可选项用Switch写法,其实就是判断可选项是否为nil
 switch age {
 case let x?:
     print(x)
 case nil:
     print("nil")
 }
 对应的if case 写法
 if case let x? = age {print(x)}

 3.可选项的本质是枚举,枚举有2项case分别是.some和.none,这个是系统的本质写法
 switch age {
 case .some(let x):
     print(x)
 case .none:
     print("nil")
 }
 对应的if case 写法
 if case .some(let x) = age {print(x)}
 
 利用可选模式判断非空
 let ages: [Int?] = [nil, 1, 2, nil, 5]
 for case let age? in ages {
     print(age)
 }//1 2 5 因为age?的这个(?)是非空的意思,所以nil的那2个值进不来
 //👆🏻 👇🏻两种for的效果是等价的, 但是👆🏻的使用了可选模式代码精简,⚠️这个就是可选模式的好处
 for item in ages { //遍历ages,不管是不是空,都放进来
     if let age = item {// 可选项绑定, 意思就是假如item是非空, 就会绑定到age上
         print(item)
     }
 }
 
 举例需求:确定num是否为特定值和是否nil
 func check(_ num: Int?) {
     switch num {
     case 2?: print("2")
     case 4?: print("4")
     case 5?: print("5")
     case _?: print("other") //2 4 5之外的非空值
     case _: print("nil") //只能是空值了
     }
 }
 check(4) //4
 check(8) //other
 check(nil) //nil

 
 7, 类型转换模式(Type-Casting Pattern )
 let num: Any = 10
 switch num {
 case is Int :// 编译器此时仍然认为num是Any类型,只是这里判断num的本质是Int
     print("is Int")
 case let n as Int: //强转Int也可以成功,但是num依旧是Any类型,只是这个n是Int类型
     print("is Int")
 default:
     break
 }
 
 class Animal { func eat() {print(type(of: self), "eat")} }
 class Dog : Animal { func run() {print(type(of: self), "run")} }
 class Cat : Animal { func jump() {print(type(of: self), "jump")} }

 func check (_ animal: Animal) {
     switch animal {
     case let dog as Dog://转成Dog类型,所以可以调父类eat方法和自己的run方法
         dog.eat()
         dog.run()
     case is Cat: //只是判断本质是不是Cat类型,并没有任何转换,所以现在animal还是Animal类型,所以只能调用父类自己的方法eat,不能调用jump方法.因为本质是Cat类型,所以调用eat方法的输出结果是Cat eat
         animal.eat()
 //假如真的需要在这里调用cat的方法,可以(animal as? Cat)?.jump()
     default: break
     }
 }
 check(Dog())//Dog eat //Dog run
 check(Cat()) //Cat eat

 8, 表达式模式(Expression Pattern)
 struct Student {//定义一个学生类,里面重写~=匹配符的方法,达到在case中自定义效果
     var score = 0, name = ""
     
     /// ⚠️[~=]: 是一个运算符,在汇编中可以观察得到Switch的case中面对复杂一点的内容就是用~=这个运算符去匹配真假
     /// ~=运算符的格式: func (pattern: , value: ) -> Bool
     /// pattern(中文:模式): case后面的内容
     /// value: Switch后面的内容
     static func ~= (pattern: Int, value: Student) -> Bool {
         //理解:我传入Switch的对象value 和 case的类型pattern 进来给你, 你来决定返回真假是否进入输出
         // 修改传入Int类型的匹配规则, 大于传入值,就返回true
         if value.score >= pattern {
             return true
         }else {
             return false
         }
     }
     
     static func ~= (pattern: Range<Int>, value: Student) -> Bool {
         // 修改传入开区间类型的匹配规则, 在这个范围内,就返回true
         pattern.contains(value.score)
     }
     
     static func ~= (pattern: ClosedRange<Int>, value: Student) -> Bool {
         // 修改传入闭区间类型的匹配规则, 在这个区间内,就返回true
         pattern.contains(value.score)
     }
 }

 var stu = Student(score: 75, name: "Jack")
 //正常情况下,stu是结构体类型,在Switch中,不可以使用Int去匹配
 switch stu {
 case 100: print(">= 100")
 case 90:  print(">= 90")
 case 80 ..< 90: print("[80, 90]]") //半开区间 80 <= 取值 <90 ,不包括90
 case 60 ... 79: print("[60, 79]]") //闭区间 60 <= 取值 <=79, 包括79
 case 0: print(">=0")
 default: break
 }

 //⚠️ 因为重写了~=,if case也可以用
 if case 100 = stu { } //就是相当于Switch stu 的 case100

 //一个元祖,左边是student对象,右边是字符串
 var info = (Student(score: 70, name: "jack"), "及格")
 //⚠️ 因为重写了~=,哪怕是别的类型比如元祖,里面的Int,也是可以直接当做case这个Int去比较
 switch info {
 case let (60, text) : print(text)
 default: break
 }//let (60, text) 的60因为~=运算符被重写了Int类型,所以这里的含义就是这个stu对象的score是否大于60,大于就是真,所以右边的text自动就会被赋值成"及格", 最终print(text)输出是 及格





 var str = "123456"
 print(str.hasPrefix("123")) //输出true //hasPrefix: 是否有这个前缀开头
 print(str.hasSuffix("456")) //输出true //hasSuffix: 是否有这个后缀结尾

 /*
  这个函数的意思:接受一个字符串作为参数, 调用这个字符串的hasPrefix判断一下是否有传进来(_ prefix: String)的前缀
  _ prefix: String : 判断是否有这个前缀
  (str: String) : 返回出去的函数所调用的参数
  */
 func hasPrefix(_ prefix: String) -> ( (String)->Bool ) {
     return {
         (str: String) -> Bool in
         str.hasPrefix(prefix)
     }
 }
 var fn = hasPrefix("123")
 print(fn("123456"))// true

 //后缀,和👆🏻前缀一样的写法,简陋版本
 func hasSuffix(_ suffix: String) -> ( (String)->Bool ) { {$0.hasSuffix(suffix)} }

 extension String {
     static func ~= (pattern: (String)-> Bool, value: String) -> Bool {
         pattern(value)
     }
 }

 switch str {
 case hasPrefix("123"): //(String)->Bool )
     print("以123开头")
 case hasSuffix("456"): //(String)->Bool )
     print("以456结尾")
 default: break
 }//以123开头 达到效果: 用Switch语法去判断字符串的开头和结尾,利用自定义表达式模式的高端写法

 //需求: 判断奇偶数, 使用Switch语法放入一个Int,2个case去判断奇偶数
 func isEven(_ i: Int) -> Bool { i % 2 == 0 } //偶数
 func isOdd(_ i: Int) -> Bool { i % 2 != 0 } //奇数
 //👆🏻是一个函数, 接受Int,返回Bool
 extension Int {
     //重写~=运算符
     static func ~= (pattern: (Int)->Bool, value: Int) -> Bool {
         pattern(value)
     }
 }

 var intA = 10
 switch intA {
 case isEven:
     print(intA,"是偶数")
 case isOdd:
     print(intA,"是奇数")
 default: break
 }//10 是偶数

 // 需求: 判断num >
 func greaterThan(_ num: Int) -> (Int)->Bool {//输入num, 返回函数类型(int)->Bool
     return {
         (i: Int) -> Bool in
         return i > num
         // { $0 > num} : 这样子写也可以,是极致的缩写,表达是返回的参数大于num
     }
 }

 var fnNumA = greaterThan(5) //方法greaterThan的参数num = 5, 返回值是(Int) -> Bool, 所以现在fnNumA是(Int) -> Bool类型
 print(fnNumA(6)) //true
 /*
  fnNumA(6) 等效于 👇🏻:
  {
     (i: Int) -> Bool in
     return i > num
  }
  把值输入后就是:
  {
     (6: Int) -> Bool in
     return i > 5
  }
  所以结果的 6>5 返回true
  */
 switch intA {
 case greaterThan(5):
     print(intA, "大于5")
 default: break
 }//10 > 5
 */

//: ## where
/*
 可以使用where为模式匹配增加匹配条件
 */
 var data = (10, "Jack")
switch data {
case let (age, _) where age > 10:
    print(data.1, "age > 10")
case let (age, _) where age > 0:
    print(data.1, "age > 0")
default: break
}//Jack age > 0

var ages = [10, 20, 30, 40, 50]
for age in ages where age > 30 {
    print(age)
}// 40 50






//: [Next](@next)
